//
// Templet.h
//
// $Id: //base/include/Templet.h
//
// Library: base
// Package: base
// Module : Templet
// Author :	Jelin,doitsjz@163.com
// Date	  :	10/30/2012
// 
// Definition of the Templet class.
//
// Copyright (c) 2012-2012, iGuard.
//

#include "RTSPMessage.h"

#include "Poco/Net/NetException.h"
#include "poco/NumberFormatter.h"
#include "poco/NumberParser.h"

#include "Poco/Timestamp.h"
#include "Poco/DateTimeFormatter.h"
#include "Poco/DateTimeFormat.h"
#include "Poco/DateTime.h"
#include "Poco/Timespan.h"
#include "poco/DateTimeParser.h"
#include "Poco/String.h"

#include <sstream>


using Poco::DateTime;
using Poco::Timespan;
using Poco::DateTimeFormat;
using Poco::DateTimeFormatter;
using Poco::DateTimeParser;

using Poco::NumberFormatter;
using Poco::NumberParser;
using Poco::icompare;
using Poco::Timestamp;
using Poco::Net::MessageException;
using Poco::Net::NoMessageException;

const std::string RTSPMessage::RTSP_1_0                  = "RTSP/1.0";
const std::string RTSPMessage::RTSP_2_0                  = "RTSP/2.0";
const std::string RTSPMessage::Accept                    = "Accept";
const std::string RTSPMessage::CSeq                      = "CSeq";
const std::string RTSPMessage::UserAgent                 = "User-Agent";
const std::string RTSPMessage::Transport                 = "Transport";
const std::string RTSPMessage::Session                   = "Session";
const std::string RTSPMessage::Range                     = "Range";

const std::string RTSPMessage::AcceptEncoding            = "Accept-Encoding";
const std::string RTSPMessage::AcceptLanguage            = "Accept-Language";
const std::string RTSPMessage::Authorization             = "Authorization";
const std::string RTSPMessage::Bandwidth                 = "Bandwidth";
const std::string RTSPMessage::Blocksize                 = "Blocksize";
const std::string RTSPMessage::CacheControl              = "Cache-Control";
const std::string RTSPMessage::Conference                = "Conference";
const std::string RTSPMessage::Connection                = "Connection";
const std::string RTSPMessage::ContentBase               = "Content-Base";
const std::string RTSPMessage::ContentEncoding           = "Content-Encoding";
const std::string RTSPMessage::ContentLanguage           = "Content-Language";
const std::string RTSPMessage::ContentLength             = "Content-Length";
const std::string RTSPMessage::ContentLocation           = "Content-Location";
const std::string RTSPMessage::ContentType               = "Content-Type";
const std::string RTSPMessage::Date                      = "Date";
const std::string RTSPMessage::Expires                   = "Expires";
const std::string RTSPMessage::From                      = "From";
const std::string RTSPMessage::Host                      = "Host";
const std::string RTSPMessage::IfMatch                   = "If-Match";
const std::string RTSPMessage::IfModifiedSince           = "If-Modified-Since";
const std::string RTSPMessage::LastModified              = "Last-Modified";
const std::string RTSPMessage::Location                  = "Location";
const std::string RTSPMessage::ProxyAuthenticate         = "Proxy-Authenticate";
const std::string RTSPMessage::ProxyRequire              = "Proxy-Require";
const std::string RTSPMessage::Referer                   = "Referer";
const std::string RTSPMessage::RetryAfter                = "Retry-After";
const std::string RTSPMessage::Require                   = "Require";
const std::string RTSPMessage::RTPInfo                   = "RTP-Info";
const std::string RTSPMessage::Scale                     = "Scale";
const std::string RTSPMessage::Speed                     = "Speed";
const std::string RTSPMessage::Timestamp                 = "Timestamp";
const std::string RTSPMessage::Vary                      = "Vary";
const std::string RTSPMessage::Via                       = "Via";
const std::string RTSPMessage::Allow                     = "Allow";
const std::string RTSPMessage::Public                    = "Public";
const std::string RTSPMessage::Server                    = "Server	";
const std::string RTSPMessage::Unsupported               = "Unsupported";
const std::string RTSPMessage::WWWAuthenticate           = "WWW-Authenticate";

//for Transport value
const std::string CRtspTransport::DESTINATION				 = "destination";
const std::string CRtspTransport::INTERLEAVED				 = "interleaved";
const std::string CRtspTransport::CLIENT_PORT				 = "client_port";
const std::string CRtspTransport::SERVER_PORT				 = "server_port";
const std::string CRtspTransport::TTL						 = "ttl";
const std::string CRtspTransport::SSRC						 = "ssrc";
const std::string CRtspTransport::MODE  					 = "mode";

//for Range value
const std::string CRange::SMPTE							 = "smpte";
const std::string CRange::NPT							 = "npt";
const std::string CRange::CLOCK							 = "clock";
const std::string CRange::TIME							 = "time";

const std::string RTSPMessage::IDENTITY_TRANSFER_ENCODING = "identity";
const std::string RTSPMessage::CHUNKED_TRANSFER_ENCODING  = "chunked";
const int         RTSPMessage::UNKNOWN_CONTENT_LENGTH     = -1;
const std::string RTSPMessage::UNKNOWN_CONTENT_TYPE;
const std::string RTSPMessage::CONTENT_LENGTH             = "Content-Length";
const std::string RTSPMessage::CONTENT_TYPE               = "Content-Type";
const std::string RTSPMessage::TRANSFER_ENCODING          = "Transfer-Encoding";
const std::string RTSPMessage::CONNECTION                 = "Connection";
const std::string RTSPMessage::CONNECTION_KEEP_ALIVE      = "Keep-Alive";
const std::string RTSPMessage::EMPTY;

const std::string RTSPMessage::APP_SDP_TYPE				  = "application/sdp";
const std::string RTSPMessage::TXT_PARA_TYPE			  = "text/parameters";

RTSPMessage::RTSPMessage():
	_version(RTSP_1_0)
{
}


RTSPMessage::RTSPMessage(const std::string& version):
	_version(version)
{
}


RTSPMessage::~RTSPMessage()
{
}


void RTSPMessage::setVersion(const std::string& version)
{
	_version = version;
}
void RTSPMessage::setDate(const std::string& date)
{
	set(Date, date);
}
void RTSPMessage::setDate()
{
    Poco::Timestamp now;
	std::string str = DateTimeFormatter::format(now, DateTimeFormat::HTTP_FORMAT);
	set(Date, str);
}
const std::string& RTSPMessage::getDate() const
{
	const std::string& date= get(Date,EMPTY);
	return date;
}

void RTSPMessage::setCSeq(int cseq)
{
	set(CSeq, NumberFormatter::format(cseq));
}

const int RTSPMessage::getCSeq() const
{
	const std::string& cseq = get(CSeq, EMPTY);
	if (!cseq.empty())
	{
		return NumberParser::parse(cseq);
	}
	return UNKNOWN_CONTENT_LENGTH;
}
void RTSPMessage::setUserAgent(const std::string& userAgent)
{
	if (userAgent.empty())
		setUserAgent();
	else	
		set(UserAgent,userAgent);
}
void RTSPMessage::setUserAgent()
{
	std::string tmp="iGuard-RTSP";
	set(UserAgent,tmp);
}

void RTSPMessage::setAccept(const std::string& mediaType)
{
	if (mediaType.empty())
		erase(Accept);
	else
		set(Accept, mediaType);

}
void RTSPMessage::setAccept()
{
	std::string mediaType = "application/sdp";
	set(Accept, mediaType);
}

const std::string& RTSPMessage::getAccept() const
{
	return get(Accept, UNKNOWN_CONTENT_TYPE);
}
void RTSPMessage::setTransport(const std::string& transport)
{
	set(Transport,transport);
}
void RTSPMessage::setTransport(const CRtspTransport& transport)
{
	std::ostringstream ostr;
	transport.write(ostr);
	std::string strTmp = ostr.str();
	setTransport(strTmp);
}
void RTSPMessage::getTransport(std::string& transport)
{
	get(Transport,transport);
}
void RTSPMessage::getTransport(CRtspTransport& transport)
{
	std::string strTrans;
	strTrans = get(Transport);
	std::istringstream istr(strTrans);
	transport.read(istr);
}
void RTSPMessage::setRange(const std::string& range)
{
	set(Range,range);
}
void RTSPMessage::setRange(const CRange& range)
{
	std::ostringstream ostr;
	range.write(ostr);
	std::string strTmp = ostr.str();
	setRange(strTmp);
}
void RTSPMessage::setSession(const std::string& session)
{
	set(Session,session);
}
const std::string& RTSPMessage::getSession() const
{
	return get(Session);
}
void RTSPMessage::setContentLength(std::streamsize length)
{
	if (length != UNKNOWN_CONTENT_LENGTH)
		set(ContentLength, NumberFormatter::format(length));
	else
		erase(ContentLength);
}

	
std::streamsize RTSPMessage::getContentLength() const
{
	const std::string& contentLength = get(ContentLength, EMPTY);
	if (!contentLength.empty())
	{
		if (sizeof(std::streamsize) == sizeof(Poco::Int64))
			return static_cast<std::streamsize>(NumberParser::parse64(contentLength));
		else
			return static_cast<std::streamsize>(NumberParser::parse(contentLength));
	}
	else return UNKNOWN_CONTENT_LENGTH;
}

void RTSPMessage::setContentType(const std::string& mediaType)
{
	if (mediaType.empty())
		erase(ContentType);
	else
		set(ContentType, mediaType);
}

const std::string& RTSPMessage::getContentType() const
{
	return get(ContentType, UNKNOWN_CONTENT_TYPE);
}

void RTSPMessage::setContentBody(const std::string& body)
{
	_body = body;
}
const std::string& RTSPMessage::getContentBody() const
{
	return _body;
}
void RTSPMessage::setMessage(const std::string& message)
{
	std::istringstream istr(message);
	read(istr);
}
void RTSPMessage::getMessage(std::string& message)
{
	std::ostringstream ostr;
	write(ostr);
	message = ostr.str();
}


CRtspTransport::CRtspTransport(int tpType)
{
	tpType ? strTPType = "RTP/AVP/TCP":strTPType = "RTP/AVP";
	strTType = "unicast";
}
CRtspTransport::~CRtspTransport()
{
}
void CRtspTransport::setInterleaved(int channel)
{
	std::string strTmp = NumberFormatter::format(channel);
	strTmp += "-";
	NumberFormatter::append(strTmp,channel+1);
	set(INTERLEAVED, strTmp);
}
void CRtspTransport::setClientPort(int port)
{
	std::string strTmp = NumberFormatter::format(port);
	strTmp += "-";
	NumberFormatter::append(strTmp,port+1);
	set(CLIENT_PORT, strTmp);
}

void CRtspTransport::setServerPort(int port)
{
	std::string strTmp = NumberFormatter::format(port);
	strTmp += "-";
	NumberFormatter::append(strTmp,port+1);
	set(SERVER_PORT, strTmp);
}

void CRtspTransport::setMode(const std::string& mode)
{
	set(MODE, mode);
}

void CRtspTransport::setTTL(int ttl)
{
	set(TTL, NumberFormatter::format(ttl));
}
void CRtspTransport::setDestination(const std::string& address)
{
	set(DESTINATION, address);
}
void CRtspTransport::setSSRC(const std::string& ssrc)
{
	set(SSRC, ssrc);
}
void MultiValue::write(std::ostream& ostr) const
{
	NameValueCollection::ConstIterator it = begin();
	while (it != end())
	{
		ostr << it->first << "=" << it->second << ";";
		++it;
	}
}

void CRtspTransport::write(std::ostream& ostr) const
{
	ostr << strTPType << ";";
	ostr << strTType  << ";";
	MultiValue::write(ostr);
}
void CRtspTransport::read(std::istream& istr)
{
	static const int eof = std::char_traits<char>::eof();
	strTPType.clear();
	strTType.clear();
	int ch = istr.get();
	if (ch == eof) throw NoMessageException();
	while (Poco::Ascii::isSpace(ch)) ch = istr.get();
	if (ch == eof) throw MessageException("No Transport Value");
	while (ch != ';'&& ch != eof ) { strTPType += (char) ch; ch = istr.get(); }
	if (ch == ';') ch = istr.get();
	while (Poco::Ascii::isSpace(ch)) ch = istr.get();
	while (ch != ';' && ch != eof) { strTType += (char) ch; ch = istr.get(); }

	if (strTPType.empty())
		strTPType = "RTP/AVP";
	if (strTType.empty())
		strTType = "unicast";

	MultiValue::read(istr);
}

void MultiValue::read(std::istream& istr)
{
	static const int eof = std::char_traits<char>::eof();
	std::streambuf& buf = *istr.rdbuf();

	std::string name;
	std::string value;
	name.reserve(32);
	value.reserve(64);
	int ch = buf.sbumpc();
	while (ch != eof && ch != ';')
	{
		name.clear();
		value.clear();
		while (ch != eof && ch != '=' && ch != ';' && name.length() < MAX_NAME_LENGTH) { name += ch; ch = buf.sbumpc(); }
		if (ch == ';') { ch = buf.sbumpc(); continue; } // ignore invalid header lines
		if (ch != '=') throw MessageException("Field name too long/no colon found");
		if (ch != eof) ch = buf.sbumpc(); // ':'
		while (ch != eof && Poco::Ascii::isSpace(ch) && ch != ';') ch = buf.sbumpc();
		while (ch != eof && ch != ';'&& value.length() < MAX_VALUE_LENGTH) { value += ch; ch = buf.sbumpc(); }
		if (ch == ';') ch = buf.sbumpc();
		else if (ch != eof)
			throw MessageException("Field value too long/no CRLF found");
		while (ch == ' ' || ch == '\t') // folding
		{
			while (ch != eof && ch != ';'&& value.length() < MAX_VALUE_LENGTH) { value += ch; ch = buf.sbumpc(); }
			if (ch == ';') ch = buf.sbumpc();
			else if (ch != eof)
				throw MessageException("Folded field value too long/no CRLF found");
		}
		Poco::trimRightInPlace(value);
		add(name, value);
	}
	istr.putback(ch);
}
CRange::CRange(int nType)
{
}
CRange::~CRange()
{
}
void CRange::seRange(std::string& strStart,std::string& strEnd)
{
}
void CRange::seRange(float fStart,float fEnd)
{
	std::string strTmp;
	if (fStart>-0.00f && fStart<0.00f)
		strTmp = "now";
	else
		strTmp = NumberFormatter::format(fStart);

	strTmp += "-";

	if (fEnd>-0.00f || fEnd>0.00f)
		NumberFormatter::append(strTmp,fEnd);

	set(NPT, strTmp);
}
void CRange::setTime(std::string& strTime)
{
	set(TIME,strTime);
}

